﻿/*  @File        : MyHelper.h
 *  @Brief       : 常用方法集合
 *  @Details     : 本头文件包含了本人软件开发过程中,经常用到的方法
 *  @Author      : HaoQinghui
 *  @Date        : 2024-04-04 21:19:37
 *  @Version     : v1.0
 *  @Copyright   : Copyright By HaoQinghui, All Rights Reserved
 *
 **********************************************************
 *
 *  @Attention   :
 *  @note 修改日志:
 *  <table>
 *  <tr><th>Date       <th>Version  <th>Author      <th>Description
 *  <tr><td>2024-04-04 <td>1.0      <td>HaoQinghui  <td>创建初始版本
 *  </table>
 *
 **********************************************************/

#ifndef MYHELPER_H
#define MYHELPER_H

#include <QtCore>
#ifdef QT_NETWORK_LIB
#include <QtNetwork>
#include <QHostAddress>
#include <QNetworkInterface>
#endif // QT_NETWORK_LIB
#ifdef QT_GUI_LIB
#include <QTextCodec>
#include <QMessageBox>
#endif

#ifdef Q_OS_WIN
#define NEWLINE             "\r\n"
#endif
#ifdef Q_OS_LINUX
#define NEWLINE             "\n"
#endif

#define GreaterThan(major, minor) \
    (QT_VERSION >= QT_VERSION_CHECK(major, minor, 0))   // 大于等于某个版本号
#define LessThan(major, minor) \
    (QT_VERSION < QT_VERSION_CHECK(major, minor, 0))    // 小于某个版本号
#if GreaterThan(5, 0) && LessThan(6, 0) && !defined(QT5)
#define QT5     true    // Qt5开发平台
#endif
#if GreaterThan(6, 0) && LessThan(7, 0) && !defined(QT6)
#define QT6     true    // Qt6开发平台
#endif

#if LessThan(6, 0)
//#pragma  execution_character_set("utf-8")
#endif

#define QDATE       qPrintable (QDate::currentDate().toString("yyyy-MM-dd"))
#define QTIME       qPrintable (QTime::currentTime().toString("HH-mm-ss"))
#define TIME        qPrintable (QTime::currentTime().toString("HH:mm:ss"))
#define TIMEMS      qPrintable (QTime::currentTime().toString("HH:mm:ss zzz"))
#define DATETIME    qPrintable (QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"))
#define DATETIMEMS  qPrintable (QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz"))
#define DATETIME_F  qPrintable (QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss"))
#define DATETIME_MS qPrintable (QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss-zzz"))

#ifdef QT_NO_DEBUG
#define LOG         qDebug().noquote() << DATETIMEMS << "DEBUG  "
#define LOGINFO     qInfo().noquote() << DATETIMEMS << "INFO  "
#define LOGERROR    qCritical().noquote() << DATETIMEMS << "ERROR  "
#else
#define LOG         qDebug().noquote() <<__FILE__<<__LINE__<<__FUNCTION__
#define LOGINFO     qInfo().noquote() << __FILE__<<__LINE__<<__FUNCTION__
#define LOGERROR    qCritical().noquote() << __FILE__<<__LINE__<<__FUNCTION__
#endif

#define AppPath qApp->applicationDirPath()
#define AppDeskWidth qApp->desktop()->availableGeometry().width()
#define AppDeskHeight qApp->desktop()->availableGeometry().height()

// 输出枚举类型的字符串, 如下需要switch整个枚举
#define CASE_STR(x)     case x : return #x; break;
/* 输出枚举举例
std::string printEnum(TT e)
{
    switch (e)
    {
    CASE_STR(OK);
    CASE_STR(NO_OK);
    CASE_STR(TEST);
    CASE_STR(HEL_WORLD);
    CASE_STR(GUESS);
    return "UNKNOW_EVENT!";
    }
}
*/

#ifdef hqh
class Q_DECL_EXPORT MyHelper
{
#else
class MyHelper
{
#endif
public:
    /**
     * @brief 设置为开机启动
     */
    static void runWithSystem(bool isAutoRun)
    {
        // 获取应用名称
        QString application_name = qApp->applicationName();
        QSettings *reg = new QSettings("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
        if (isAutoRun) {
            // 找到应用的目录
            QString application_path = qApp->applicationFilePath();
            // 写入注册表
            reg->setValue(application_name, application_path.replace("/", "\\"));
        } else {
            // 从注册表中删除
            reg->remove(application_name);
        }
    }

    /**
     * @brief 设置系统编码
     */
    static void setCode()
    {
#ifdef QT_GUI_LIB
#if LessThan(6, 0)
#if _MSC_VER
        QTextCodec *codec = QTextCodec::codecForName("gbk");
#else
        QTextCodec *codec = QTextCodec::codecForName("UTF-8");
#endif // _MSC_VER
        QTextCodec::setCodecForLocale(codec);
#elif LessThan(5, 0)
        // 只有Qt4支持以下两个函数
        QTextCodec::setCodecForCStrings(codec);
        QTextCodec::setCodecForTr(codec);
#else
        QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
#endif
#endif // QT_GUI_LIB
    }

    /**
     * @brief 延时
     */
    inline static void sleep(int sec)
    {
        QTime dieTime = QTime::currentTime().addMSecs(sec);
        while (QTime::currentTime() < dieTime) {
            QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
        }
    }

    /**
     * @brief 设置单例模式
     * @param app_name  AppName
     * @return 是否未启动
     */
    static bool setAppSingleton(const QString &app_name)
    {
        // 利用共享内存判断程序是否重复启动
        auto m_sharedMemory = new QSharedMemory(app_name);
        if (m_sharedMemory->attach()) {
            if (!m_sharedMemory->detach()) {
                qDebug().noquote() << m_sharedMemory->key() << "detach failed!";
    #ifdef QT_GUI_LIB
                QMessageBox::critical(nullptr,
                                      QObject::tr("Error"),
                                      m_sharedMemory->key() + QObject::tr(" detach failed!"));
    #endif // QT_GUI_LIB
                return false;
            }
        }
        if (!m_sharedMemory->create(1)) {
    #ifdef Q_OS_WIN
            qInfo().noquote() << m_sharedMemory->key() << " already exists, the system can not open more!";
    #else
            qInfo().noquote() << m_sharedMemory->key() << " already exists, please use ps -ef | grep DisorderFetch3D to check!";
    #endif

            qInfo().noquote() << "sharedMemory error type: " << m_sharedMemory->error() << m_sharedMemory->isAttached();
            qInfo().noquote() << "sharedMemory ERROR: " << m_sharedMemory->errorString();

    #ifdef QT_GUI_LIB
    #ifdef Q_OS_WIN
            QMessageBox::critical(nullptr,
                                  QObject::tr("Error"),
                                  m_sharedMemory->key() + QObject::tr(" already exists, the system can not open more!"));
    #else
            QMessageBox::critical(nullptr,
                                  QObject::tr("Error"),
                                  m_sharedMemory->key() + QObject::tr(" exist, please use ps -ef | grep DisorderFetch3D to check!"));
    #endif // Q_OS_WIN
    #endif // QT_GUI_LIB
            return false;
        }

        return true;
    }

    /**
     * @brief 是否为数字
     */
    inline static bool isNum(const QString &str)
    {
        bool isret;
        str.toDouble(&isret);
        return isret;
    }
    /**
     * @brief 是否为整数
     */
    inline static bool isInt(const QString &str)
    {
        bool isret;
        str.toInt(&isret);
        return isret;
    }
    /**
     * @brief 是否为手机号码
     */
    inline static bool isPhone(const QString &str)
    {
        QString pattern = "^1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\\d{8}$";
#if QT6
        static auto expression = QRegularExpression(pattern);
        return expression.match(str).hasMatch();
#else
        static auto expression = QRegExp(pattern);
        return expression.exactMatch(str);
#endif
    }
    /**
     * @brief 是否为电子邮箱
     */
    inline static bool isEmail(const QString &str)
    {
        QString pattern = "^[a-zA-Z0-9_\\.\\-]+\\@[a-zA-Z0-9_\\.-]+\\.[a-zA-Z]{2,6}$";
        //QString pattern("^[a-zA-Z0-9_\\+-]+(\\.[a-zA-Z0-9_\\+-]+)*@[a-zA-Z0-9_\\-]+(\\.[a-zA-Z0-9_\\-]+)*\\.[a-zA-Z]{2,6}$");
#if QT6
        static auto expression = QRegularExpression(pattern);
        return expression.match(str).hasMatch();
#else
        static auto expression = QRegExp(pattern);
        return expression.exactMatch(str);
#endif
    }
    /**
     * @brief 是否为有效用户名
     */
    inline static bool isUserName(const QString &str)
    {
        QString pattern = "^[0-9a-zA-Z_]{1,}$";
#if QT6
        static auto expression = QRegularExpression(pattern);
        //return expression.match(str).hasMatch();
#else
        static auto expression = QRegExp(pattern);
        //return expression.exactMatch(str);
#endif
        return str.contains(expression);
    }
    /**
     * @brief 是否全部是中文
     */
    inline static bool isChineseName(const QString &str)
    {
        QString pattern = "^[\u4e00-\u9fa5]+$";
#if QT6
        static auto expression = QRegularExpression(pattern);
#else
        static auto expression = QRegExp(pattern);
#endif
        return str.contains(expression);
    }

#ifdef QT_NETWORK_LIB
    /**
     * @brief 是否有效的IP地址
     */
    inline static bool isIPAddress(const QString &str, QHostAddress &ipAddress)
    {
        if (0 == str.compare("any", Qt::CaseInsensitive))
            ipAddress = QHostAddress::Any;
        else
            ipAddress = QHostAddress(str);
        return !ipAddress.isNull();
    }

    /**
     * @brief 获取本机的所有网卡IP
     */
    static QStringList getHostIpAddress()
    {
        QStringList tmpList;
        // 这里获取的是本机所有的适配器网络ip
        QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
        // 获取第一个本主机的IPv4地址
        int nListSize = ipAddressesList.size();
        for (int i = 0; i < nListSize; ++i) {
            if (ipAddressesList.at(i) != QHostAddress::LocalHost && ipAddressesList.at(i).toIPv4Address()) {
                tmpList.append(ipAddressesList.at(i).toString());
            }
        }

        if (!tmpList.contains("127.0.0.1"))
            tmpList.insert(0, "127.0.0.1");
        tmpList.insert(0, "Any");
        return tmpList;
    }
    /**
     * @brief 获取本地的所有网卡信息
     * @param nameReadableLst   硬件名
     * @param macLst            MAC地址
     * @param ipLst             IP地址
     * @param netmaskLst        子网掩码
     * @param broadcastLst      广播地址
     * @param nameLst           网卡名
     * @param typeLst           是否是有线网（或无线网）
     */
    static void getNetcardInfo(QStringList &nameReadableLst, QStringList &macLst, QStringList &ipLst, QStringList &netmaskLst,
                               QStringList &broadcastLst, QStringList &nameLst, QList<QNetworkInterface::InterfaceType> &typeLst)
    {
        nameReadableLst.clear();
        macLst.clear();
        ipLst.clear();
        netmaskLst.clear();
        broadcastLst.clear();
        nameLst.clear();
        typeLst.clear();

        QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
        foreach (const QNetworkInterface &it, interfaces) {
            if (QNetworkInterface::Loopback == it.type())
                continue;

            // 选择已启用的网卡
            if (QNetworkInterface::IsRunning == (it.flags() & QNetworkInterface::IsRunning)) {
                // 获取名称和mac地址
                nameReadableLst.append(it.humanReadableName());
                macLst.append(it.hardwareAddress());
                nameLst.append(it.name());
                typeLst.append(it.type());

                // 获取型号、ip地址、子网掩码、网关
                QList<QNetworkAddressEntry> entryList = it.addressEntries();//获取ip地址和子网掩码和广播地址
                foreach(QNetworkAddressEntry entry, entryList) {
                    if (entry.broadcast().toString().isEmpty()) {
                        continue;
                    }
                    // 遍历ip条目列表
                    // 获取ip
                    ipLst.append(entry.ip().toString());
                    // 获取子网掩码
                    netmaskLst.append(entry.netmask().toString());
                    // 获取广播地址
                    broadcastLst.append(entry.broadcast().toString());
                }
            }
        }
        Q_ASSERT_X(nameReadableLst.size() == macLst.size() == ipLst.size() == netmaskLst.size(), "MyHelper::getNetcardInfo",
                   "获取的设备信息数量不对等");
        Q_ASSERT_X(netmaskLst.size() == broadcastLst.size() == nameLst.size() == typeLst.size(), "MyHelper::getNetcardInfo",
                   "获取的设备信息数量不对等");
    }
#else
    /**
     * @brief 是否有效的IP地址
     */
    inline static bool isIPAddress(const QString &str)
    {
        QString pattern = "^((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$";
#if QT6
        static auto expression = QRegularExpression(pattern);
#else
        static auto expression = QRegExp(pattern);
#endif
        return str.contains(expression);
    }
#endif

    /**
    * @brief 设置网卡信息
    * @param netDevName 网卡名
    * @param ip         IP地址
    * @param ipNetmask  网关地址
    * @return   返回消息
    */
    static QString updateNetInfo(const QString &netDevName, const QString &ip, const QString &ipNetmask)
    {
#ifdef Q_OS_WIN
        QString ipmaskgateway = QString("netsh interface ip set address name = \"%1\" source=static addr=%2 mask=%3")
                .arg(netDevName, ip, ipNetmask);
        LOG << "cmd = " << ipmaskgateway;
        QStringList arguments1;
        arguments1 << "/c" << ipmaskgateway;
        QProcess p1;
        p1.start("cmd", arguments1);
        p1.waitForFinished(3000);
        auto tmp = QString::fromLocal8Bit(p1.readAllStandardOutput());
        LOG << "cmd exec result: " << tmp;
        return tmp;
#else
        QString cmd = "sudo -S ifconfig ";
        cmd += netDevName + " " + ip;
        cmd += " netmask " + ipNetmask + " \n";

        LOG << "cmd" << cmd;
        return cmd;
#endif
    }

    /**
     * @brief 启动一个进程
     */
    static bool startProcess(const QString &processPath)
    {
        // 路径带空格,使用带参模式,可以启动
        // process->start("C:/Program Files/Exe/111.exe", QStringList("C:/Program Files/Exe/111.exe"));
        return QProcess::startDetached(processPath);
    }

    /**
     * @brief 读取json文件
     * @param jsonFilePath  json文件路径
     * @param jsonObj       读取到的json对象
     * @param isPrint       是否需要打印到LOG中
     * @param errString     读取错误的原因
     * @return 是否读取成功
     */
    static bool readJsonFile(const QString &jsonFilePath, QJsonObject &jsonObj, bool isPrint, QString &errString)
    {
        errString.clear();
        if (jsonFilePath.isEmpty()) {
            // 打开Json文件失败! 原因: Json文件的地址为空!";
            errString = QObject::tr("ReadJsonFile: Open json file failed. Reason: jsonFile path is empty!");
            LOGERROR << errString;
            return false;
        }

        QFile file(jsonFilePath);
        if (!file.open(QIODevice::ReadOnly)) {

            // 打开json文件失败! 原因:
            errString = QObject::tr("ReadJsonFile: Open %1 failed. Reason: %2")
                    .arg(jsonFilePath, file.errorString());
            LOGERROR << errString;
            return false;
        }

        QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
        if (doc.isNull()) {
            // file的内容转Json配置文件失败! 原因:
            errString = QObject::tr("ReadJsonFile: FromJson %1 failed. Reason: %2")
                    .arg(jsonFilePath, file.errorString());
            LOGERROR << errString;
            return false;
        }

        if (isPrint) {
            LOG << doc.toJson(QJsonDocument::Indented);
        }

        jsonObj = doc.object();
        if (jsonObj.isEmpty()) {
            // Json配置文件%1为空
            errString = QObject::tr("ReadJsonFile: JsonObj is empty. File %1.").arg(jsonFilePath);
            LOGERROR << errString;
            return false;
        }
        return true;
    }
    /**
     * @brief 保存json文件(只保留一份备份)
     * @param jsonFilePath  json文件路径
     * @param jsonObj       待保存的json对象
     * @param errString     保存错误的原因
     * @return 是否保存成功
     */
    static bool saveJsonFile(const QString &jsonFilePath, const QJsonObject &jsonObj, QString &errString)
    {
        errString.clear();
        QString temp = jsonFilePath + QDateTime::currentDateTime().toString("yyyy-MM-dd HH_mm_ss") + ".tmp";
        QFile file(temp);
        if (!file.open(QIODevice::WriteOnly)) {
            // SaveJsonFile: 打开临时文件%1失败! 原因:
            errString = QObject::tr("SaveJsonFile: failed to write temp file %1. Reason: %2").arg(temp, file.errorString());
            LOGERROR << errString;
            return false;
        }

        QTextStream stream(&file);
#if QT6
        stream.setEncoding(QStringConverter::Utf8);
#else
        stream.setCodec("UTF-8");
#endif
        QJsonDocument doc(jsonObj);
        stream << doc.toJson(); // 写入文件
        file.close();

        // 备份当前配置
        QString backup = jsonFilePath + ".back";
        // 移除当前的备份文件
        if (QFile::exists(backup) && !QFile::remove(backup)) {
            // SaveJsonFile: 移除旧的备份文件%1失败!
            errString = QObject::tr("SaveJsonFile: failed to remove backup file %1").arg(backup);
            LOGERROR << errString;
            return false;
        }

        // 把当前的配置备份
        if (QFile::exists(jsonFilePath) && !QFile::rename(jsonFilePath, backup)) {
            // SaveJsonFile: 重新创建备份文件失败
            errString = QObject::tr("SaveJsonFile: failed to create backup file %1").arg(backup);
            LOGERROR << errString;
            return false;
        }

        // 临时文件转成正式文件
        if (!file.rename(jsonFilePath)) {
            // 重命名临时文件失败
            errString = QObject::tr("SaveJsonFile: failed to rename temp file %1").arg(temp);
            LOGERROR << errString;
            return false;
        }

        // 同步文件系统
        QProcess::execute("sync");
        return true;
    }
    /**
     * @brief 保存json文件(可保留多份备份)
     * @param jsonFilePath  json文件路径
     * @param jsonObj       待保存的json对象
     * @param errString     保存错误的原因
     * @param backupCount   保留几份备份
     * @return 是否保存成功
     */
    static bool saveJsonFile(const QString &jsonFilePath, const QJsonObject &jsonObj, QString &errString, int backupCount)
    {
        errString.clear();
        QString temp = jsonFilePath + ".tmp";
        QFile file(temp);
        if (!file.open(QIODevice::WriteOnly)) {
            // SaveJsonFile: 打开临时文件%1失败! 原因:
            errString = QObject::tr("SaveJsonFile: failed to write temp file %1. Reason: %2").arg(temp, file.errorString());
            LOGERROR << errString;
            return false;
        }

        QTextStream stream(&file);
#if QT6
        stream.setEncoding(QStringConverter::Utf8);
#else
        stream.setCodec("UTF-8");
#endif
        QJsonDocument doc(jsonObj);
        stream << doc.toJson(); // 写入文件
        file.close();

        QFileInfo jsonFileInfo(jsonFilePath);
        QString absolutePath = jsonFileInfo.absolutePath();
        QString completeBaseName = jsonFileInfo.completeBaseName();
        // 备份当前配置
        QString backup = absolutePath + "/" + completeBaseName +
                jsonFileInfo.birthTime().toString("_yyyyMMddHHmmss") + ".json";
        // 重命名当前的配置文件
        if (QFile::exists(jsonFilePath) && !QFile::rename(jsonFilePath, backup)) {
            errString = QObject::tr("Rename the current json file to %1 failed").arg(backup);
            LOGERROR << errString;
            return false;
        }

        // 临时文件转成正式文件
        if (!file.rename(jsonFilePath)) {
            // 重命名临时文件失败
            errString = QObject::tr("SaveJsonFile: failed to rename temp file %1").arg(temp);
            LOGERROR << errString;
            return false;
        }

        // 检查所有的配置文件
        auto backupList = findFiles(QFileInfo(jsonFilePath).absolutePath(), QStringList() << "*.json", false);
        for (auto item : backupList) {
            if (!item.contains(completeBaseName + "_")) {
                backupList.removeAll(item);
            }
        }
        // 给lish排序
        std::sort(backupList.begin(), backupList.end());
        // 移除最老的备份文件
        while (backupList.size() > backupCount) {
            QFile(backupList.first()).remove();
            backupList.removeFirst();
        }

        // 同步文件系统
        QProcess::execute("sync");
        return true;
    }

    /**
     * @brief 检查进程是否已启动
     */
    static bool checkProcess(const QString &processName)
    {
        QProcess process;
#ifdef Q_OS_WIN
        /***
            tasklist 查看系统任务列表
            /fi (filter) 显示一系列符合筛选器指定的进程
            eq 相等
            imagename 映像名称

            eg: tasklist /fi "imagename eq QtCreator.exe"
            映像名称                       PID 会话名              会话#       内存使用
            ========================= ======== ================ =========== ============
            qtcreator.exe                22188 Console                    1    133,732 K
            qtcreator.exe                21008 Console                    1    189,648 K
        ***/
        QString cmd = QString("tasklist /fi \"imagename eq %1\"").arg(processName);
        process.start(cmd);
#else
        QStringList arguments;
        arguments << QString("-c");  // 需要加个-c参数命令才能执行
        arguments << QString("ps -A | grep %1").arg(processName);
        process.start("bash", arguments);
#endif

        process.waitForFinished();
        QString result = QString::fromLocal8Bit(process.readAllStandardOutput());
        if (result.contains(processName, Qt::CaseInsensitive)) {
            return true;
        }

        return false;
    }

    /**
     * @brief 检查路径是否存在,不存在则创建
     */
    static bool checkWorkPath(const QString &workPath)
    {
        QDir dir(workPath);
        if (!dir.exists()) {
            if (!dir.mkpath(dir.path())) {
                qWarning().noquote() << QString("CreateWorkPath: mkpath path error!") << dir.absolutePath();
                return false;
            }
            qDebug().noquote() << QString("CreateWorkPath: Start mkpath path :") << dir.absolutePath();
        }

        return dir.exists();
    }

    /**
     * @brief 拷贝文件夹下的所有子文件夹和文件
     * @param sourceDir         源路径
     * @param destinationDir    目标路径
     * @param cpErrIsBreak      拷贝文件错误时是否跳出
     * @return 是否拷贝成功
     */
    static bool copyDirectory(const QString &sourceDir, const QString &destinationDir, bool cpErrIsBreak = false)
    {
        QDir sourceDirectory(sourceDir);
        QDir destinationDirectory(destinationDir);

        if (!destinationDirectory.exists()) {
            if (!destinationDirectory.mkpath(".")) {
                LOGERROR << "Cannot create directory:" << destinationDir;
                return false;
            }
        }

        QFileInfoList fileInfoList = sourceDirectory.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
        for (int i = 0; i < fileInfoList.size(); ++i) {
            QFileInfo fileInfo = fileInfoList.at(i);
            QString fileName = fileInfo.fileName();
            QString destFilePath = destinationDir + "/" + fileName;

            if (fileInfo.isDir()) {
                if (!copyDirectory(fileInfo.absoluteFilePath(), destFilePath)) {
                    return false;
                }
            } else {
                if (!QFile::copy(fileInfo.absoluteFilePath(), destFilePath)) {
                    LOGERROR << "Cannot copy file to " << destFilePath;
                    if (cpErrIsBreak)
                        return false;
                }
            }
        }

        return true;
    }

    /**@brief       : 查找路径下对应格式的所有文件(默认不查找子文件夹)
     * @details     : 可以选择查找文件,也可以选择是否深入子文件夹中
     * @author      : HaoQinghui
     * @param       : filePath: 查找的路径
     *              : filters: 筛选文件格式(如*.jpg、.png)
     *              : isGoInDir: 是否查找子文件夹
     * @return      : 查找结果
     * @date        : 2024-04-04 21:23:20
     * @modify      : 2024-04-04 21:23:20   HaoQinghui  加入是否递归选项
     * @modify      : 2024-05-28 09:30:52   HaoQinghui  修改文件类型过滤器
     */
    static QStringList findFiles(const QString &filePath, const QStringList &filters, bool isGoInDir = false)
    {
        // 获取所选文件类型过滤器
        QStringList filtersTmp;
        foreach (auto item, filters) {
            if (item.startsWith(".")) {
                filtersTmp << "*" + item;
            } else if (!item.startsWith("*.")) {
                filtersTmp << "*." + item;
            } else {
                filtersTmp << item;
            }
        }

        QStringList fileList;
        // 定义迭代器并设置过滤器
        if (!isGoInDir) {
            QDirIterator iter(filePath, filtersTmp);

            while(iter.hasNext()) {
                iter.next();
                fileList.append(iter.filePath());
            }
        } else {
            QDirIterator iter(filePath, filtersTmp, QDir::Files | QDir::NoSymLinks,
                              QDirIterator::Subdirectories);

            while(iter.hasNext()) {
                iter.next();
                fileList.append(iter.filePath());
            }
        }

        return fileList;
    }
    /**@brief       : 查找路径下指定的文件夹名(默认不查找子文件夹)
     * @details     : 可以选择查找当前路径下的文件夹,也可以选择是否深入子文件夹中
     * @author      : HaoQinghui
     * @param       : filePath: 查找的路径
     *              : filter: 指定字符串
     *              : isGoInDir: 是否查找子文件夹
     * @return      : 查找结果
     * @date        : 2024-04-04 21:23:20
     * @modify      : 2024-04-04 21:23:20   HaoQinghui  加入是否递归选项
     * @modify      : 2024-05-28 09:30:52   HaoQinghui  修改文件类型过滤器
     */
    static QStringList findFolders(const QString &filePath, const QString &filter, bool isGoInDir = false)
    {
        if (filter.contains("/") || filter.contains("\\")) {
            return QStringList();
        }
        // QDir的路径一定要是全路径,相对路径会有错误
        QDir dir(filePath);
        if (!dir.exists()) {
            return QStringList();
        }

        // 取到所有的文件和文件名,去掉.和..文件夹
        dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
        dir.setSorting(QDir::DirsFirst);
        // 将其转化为一个list
        QFileInfoList list = dir.entryInfoList();LOG<<list;
        if (!list.isEmpty()) {
            return QStringList();
        }

        QStringList retList;

        // 采用递归算法
        foreach (const auto fileInfo, list) {
            if (fileInfo.isDir()) {
                if (fileInfo.absolutePath().contains(filter)) {
                    retList.append(fileInfo.absolutePath());
                }

                if (isGoInDir) {
                    QStringList tmpList = findFolders(fileInfo.absolutePath(), filter, isGoInDir);
                    if (!tmpList.isEmpty()) {
                        foreach (auto tmp, tmpList) {
                            retList.append(tmp);
                        }
                    }
                }
            }
        }

        return retList;
    }

    /**@brief 设置系统日期时间 */
    static void setSystemDateTime(QString year, QString month, QString day, QString hour, QString min, QString sec)
    {
#ifdef Q_OS_WIN
        QProcess p(0);
        p.start("cmd");
        p.waitForStarted();
        p.write(QString("date %1-%2-%3\n").arg(year).arg(month).arg(day).toLatin1());
        p.closeWriteChannel();
        p.waitForFinished(1000);
        p.close();
        p.start("cmd");
        p.waitForStarted();
        p.write(QString("time %1:%2:%3.00\n").arg(hour).arg(min).arg(sec).toLatin1());
        p.closeWriteChannel();
        p.waitForFinished(1000);
        p.close();
#else
        QString cmd = QString("date %1%2%3%4%5.%6").arg(month).arg(day).arg(hour).arg(min).arg(year).arg(sec);
        system(cmd.toLatin1());
        system("hwclock -w");
#endif
    }

    /**@brief 字节转16进制 */
    inline static QByteArray byteArrayToHexStr(const QByteArray &data)
    { return data.toHex(' ').toUpper(); }
    /**@brief 16进制转字节 */
    inline static QByteArray hexStrToByteArray(const QByteArray &hexStr)
    {
        auto tmp = hexStr;
        auto byte = tmp.replace(" ", "");
        return QByteArray::fromHex(tmp);
    }

    /**@brief 字节转ASCII码 */
    static QString byteArrayToAsciiStr(const QByteArray &data)
    {
        QString temp;
        int len = data.size();

        for (int i = 0; i < len; i++) {
            //0x20为空格,空格以下都是不可见字符
            char b = data.at(i);

            if (0x00 == b) {
                temp += QString("\\NUL");
            } else if (0x01 == b) {
                temp += QString("\\SOH");
            } else if (0x02 == b) {
                temp += QString("\\STX");
            } else if (0x03 == b) {
                temp += QString("\\ETX");
            } else if (0x04 == b) {
                temp += QString("\\EOT");
            } else if (0x05 == b) {
                temp += QString("\\ENQ");
            } else if (0x06 == b) {
                temp += QString("\\ACK");
            } else if (0x07 == b) {
                temp += QString("\\BEL");
            } else if (0x08 == b) {
                temp += QString("\\BS");
            } else if (0x09 == b) {
                temp += QString("\\HT");
            } else if (0x0A == b) {
                temp += QString("\\LF");
            } else if (0x0B == b) {
                temp += QString("\\VT");
            } else if (0x0C == b) {
                temp += QString("\\FF");
            } else if (0x0D == b) {
                temp += QString("\\CR");
            } else if (0x0E == b) {
                temp += QString("\\SO");
            } else if (0x0F == b) {
                temp += QString("\\SI");
            } else if (0x10 == b) {
                temp += QString("\\DLE");
            } else if (0x11 == b) {
                temp += QString("\\DC1");
            } else if (0x12 == b) {
                temp += QString("\\DC2");
            } else if (0x13 == b) {
                temp += QString("\\DC3");
            } else if (0x14 == b) {
                temp += QString("\\DC4");
            } else if (0x15 == b) {
                temp += QString("\\NAK");
            } else if (0x16 == b) {
                temp += QString("\\SYN");
            } else if (0x17 == b) {
                temp += QString("\\ETB");
            } else if (0x18 == b) {
                temp += QString("\\CAN");
            } else if (0x19 == b) {
                temp += QString("\\EM");
            } else if (0x1A == b) {
                temp += QString("\\SUB");
            } else if (0x1B == b) {
                temp += QString("\\ESC");
            } else if (0x1C == b) {
                temp += QString("\\FS");
            } else if (0x1D == b) {
                temp += QString("\\GS");
            } else if (0x1E == b) {
                temp += QString("\\RS");
            } else if (0x1F == b) {
                temp += QString("\\US");
            } else if (0x7F == b) {
                temp += QString("\\x7F");
            } else if (0x5C == b) {
                temp += QString("\\x5C");
            } else if (0x20 >= b) {
                temp += QString("\\x%1").arg(decimalToStrHex((quint8)b));
            } else {
                temp += QString("%1").arg(b);
            }
        }

        return temp.trimmed();
        // return QByteArray(temp.trimmed()).toHex();
    }
    /**@brief ASCII码转字节 */
    static QByteArray asciiStrToByteArray(QString str)
    {
        QByteArray buffer;
        int len = str.length();
        QString letter;
        QString hex;

        for (int i = 0; i < len; i++) {
            letter = str.at(i);

            if (letter == "\\") {
                i++;
                letter = str.mid(i, 1);

                if (letter == "x") {
                    i++;
                    hex = str.mid(i, 2);
                    buffer.append(strHexToDecimal(hex));
                    i++;
                    continue;
                } else if (letter == "N") {
                    i++;
                    hex = str.mid(i, 1);

                    if (hex == "U") {
                        i++;
                        hex = str.mid(i, 1);

                        if (hex == "L") {           // NUL=0x00
                            buffer.append((char)0x00);
                            continue;
                        }
                    } else if (hex == "A") {
                        i++;
                        hex = str.mid(i, 1);

                        if (hex == "K") {           // NAK=0x15
                            buffer.append(0x15);
                            continue;
                        }
                    }
                } else if (letter == "S") {
                    i++;
                    hex = str.mid(i, 1);

                    if (hex == "O") {
                        i++;
                        hex = str.mid(i, 1);

                        if (hex == "H") {           // SOH=0x01
                            buffer.append(0x01);
                            continue;
                        } else {                    // SO=0x0E
                            buffer.append(0x0E);
                            i--;
                            continue;
                        }
                    } else if (hex == "T") {
                        i++;
                        hex = str.mid(i, 1);

                        if (hex == "X") {           // STX=0x02
                            buffer.append(0x02);
                            continue;
                        }
                    } else if (hex == "I") {        // SI=0x0F
                        buffer.append(0x0F);
                        continue;
                    } else if (hex == "Y") {
                        i++;
                        hex = str.mid(i, 1);

                        if (hex == "N") {           // SYN=0x16
                            buffer.append(0x16);
                            continue;
                        }
                    } else if (hex == "U") {
                        i++;
                        hex = str.mid(i, 1);

                        if (hex == "B") {           // SUB=0x1A
                            buffer.append(0x1A);
                            continue;
                        }
                    }
                } else if (letter == "E") {
                    i++;
                    hex = str.mid(i, 1);

                    if (hex == "T") {
                        i++;
                        hex = str.mid(i, 1);

                        if (hex == "X") {           // ETX=0x03
                            buffer.append(0x03);
                            continue;
                        } else if (hex == "B") {    // ETB=0x17
                            buffer.append(0x17);
                            continue;
                        }
                    } else if (hex == "O") {
                        i++;
                        hex = str.mid(i, 1);

                        if (hex == "T") {           // EOT=0x04
                            buffer.append(0x04);
                            continue;
                        }
                    } else if (hex == "N") {
                        i++;
                        hex = str.mid(i, 1);

                        if (hex == "Q") {           // ENQ=0x05
                            buffer.append(0x05);
                            continue;
                        }
                    } else if (hex == "M") {        // EM=0x19
                        buffer.append(0x19);
                        continue;
                    } else if (hex == "S") {
                        i++;
                        hex = str.mid(i, 1);

                        if (hex == "C") {           // ESC=0x1B
                            buffer.append(0x1B);
                            continue;
                        }
                    }
                } else if (letter == "A") {
                    i++;
                    hex = str.mid(i, 1);

                    if (hex == "C") {
                        i++;
                        hex = str.mid(i, 1);

                        if (hex == "K") {           // ACK=0x06
                            buffer.append(0x06);
                            continue;
                        }
                    }
                } else if (letter == "B") {
                    i++;
                    hex = str.mid(i, 1);

                    if (hex == "E") {
                        i++;
                        hex = str.mid(i, 1);

                        if (hex == "L") {           // BEL=0x07
                            buffer.append(0x07);
                            continue;
                        }
                    } else if (hex == "S") {        // BS=0x08
                        buffer.append(0x08);
                        continue;
                    }
                } else if (letter == "C") {
                    i++;
                    hex = str.mid(i, 1);

                    if (hex == "R") {               // CR=0x0D
                        buffer.append(0x0D);
                        continue;
                    } else if (hex == "A") {
                        i++;
                        hex = str.mid(i, 1);

                        if (hex == "N") {           // CAN=0x18
                            buffer.append(0x18);
                            continue;
                        }
                    }
                } else if (letter == "D") {
                    i++;
                    hex = str.mid(i, 1);

                    if (hex == "L") {
                        i++;
                        hex = str.mid(i, 1);

                        if (hex == "E") {           // DLE=0x10
                            buffer.append(0x10);
                            continue;
                        }
                    } else if (hex == "C") {
                        i++;
                        hex = str.mid(i, 1);

                        if (hex == "1") {           // DC1=0x11
                            buffer.append(0x11);
                            continue;
                        } else if (hex == "2") {    // DC2=0x12
                            buffer.append(0x12);
                            continue;
                        } else if (hex == "3") {    // DC3=0x13
                            buffer.append(0x13);
                            continue;
                        } else if (hex == "4") {    // DC2=0x14
                            buffer.append(0x14);
                            continue;
                        }
                    }
                } else if (letter == "F") {
                    i++;
                    hex = str.mid(i, 1);

                    if (hex == "F") {               // FF=0x0C
                        buffer.append(0x0C);
                        continue;
                    } else if (hex == "S") {        // FS=0x1C
                        buffer.append(0x1C);
                        continue;
                    }
                } else if (letter == "H") {
                    i++;
                    hex = str.mid(i, 1);

                    if (hex == "T") {               // HT=0x09
                        buffer.append(0x09);
                        continue;
                    }
                } else if (letter == "L") {
                    i++;
                    hex = str.mid(i, 1);

                    if (hex == "F") {               // LF=0x0A
                        buffer.append(0x0A);
                        continue;
                    }
                } else if (letter == "G") {
                    i++;
                    hex = str.mid(i, 1);

                    if (hex == "S") {               // GS=0x1D
                        buffer.append(0x1D);
                        continue;
                    }
                } else if (letter == "R") {
                    i++;
                    hex = str.mid(i, 1);

                    if (hex == "S") {               // RS=0x1E
                        buffer.append(0x1E);
                        continue;
                    }
                } else if (letter == "U") {
                    i++;
                    hex = str.mid(i, 1);

                    if (hex == "S") {               // US=0x1F
                        buffer.append(0x1F);
                        continue;
                    }
                } else if (letter == "V") {
                    i++;
                    hex = str.mid(i, 1);

                    if (hex == "T") {               // VT=0x0B
                        buffer.append(0x0B);
                        continue;
                    }
                } else if (letter == "\\") {
                    // 如果连着的是多个\\则对应添加\对应的16进制0x5C
                    buffer.append(0x5C);
                    continue;
                } else {
                    // 将对应的\[前面的\\也要加入
                    buffer.append(0x5C);
                    buffer.append(letter.toLatin1());
                    continue;
                }
            }
            buffer.append(str.mid(i, 1).toLatin1());
        }

        return buffer;
    }

    static QString decimalToStrHex(int decimal)
    {
        QString temp = QString::number(decimal, 16);

        if (temp.length() == 1) {
            temp = "0" + temp;
        }

        return temp.toUpper();
    }
    static int strHexToDecimal(const QString &strHex)
    {
        bool ok;
        return strHex.toInt(&ok, 16);
    }
};
#endif // MYHELPER_H
